package bfgca;
import java.util.*;
import java.awt.*;
import javax.swing.*;

import java.awt.event.*;

public class MapComponent extends JComponent implements Scrollable, 
MouseMotionListener, MouseListener{
  
  private Map theMap;
  private final int MAP_SIZE_X = 400;
  private final int MAP_SIZE_Y = 400;
  private final int MAP_RES = 40;    //map will be sectioned into squares
  private final int PLANET_SIZE = MAP_RES/2;
  private final int PADDING = 4;
  private BasicStroke SystemStroke, OrbitStroke, PlanetStroke;
  private StarSystem SystemTable[][];
  private boolean drawSector; //if false, draw the currently selected system
  private Coordinate selection;
  private ToolTipManager toolTips;
  private Coordinate planetPositions[] = {
      new Coordinate(2, 16),
      new Coordinate(10, 19),
      new Coordinate(18, 10),
      new Coordinate(10, 3),
      new Coordinate(5, 7),
      new Coordinate(6, 13),
      new Coordinate(15, 11),
      new Coordinate(12, 8),
  };
  
  public MapComponent (){
	  this.setPreferredSize(new Dimension(MAP_SIZE_X, MAP_SIZE_Y));
	  this.SystemStroke = new BasicStroke(2);
	  this.OrbitStroke = new BasicStroke(1, BasicStroke.CAP_ROUND, 
	      BasicStroke.JOIN_ROUND, 10, new float[]{1, 10}, 0);
	  this.PlanetStroke = new BasicStroke(2);
	  this.theMap = new Map();
	  this.addMouseMotionListener(this);
	  this.addMouseListener(this);
	  this.SystemTable = new StarSystem[MAP_SIZE_X/MAP_RES][MAP_SIZE_Y/MAP_RES];
	  this.drawSector = true;
	  this.selection = new Coordinate(0,0);
	  initialize();
  }
  
  public MapComponent (Map m ){
	  this.theMap = m;
    this.SystemStroke = new BasicStroke(2);
    this.OrbitStroke = new BasicStroke(1);
    this.PlanetStroke = new BasicStroke(2);
	  this.addMouseMotionListener(this);
	  this.addMouseListener(this);
    this.SystemTable = new StarSystem[MAP_SIZE_X/MAP_RES][MAP_SIZE_Y/MAP_RES];
    this.drawSector = true;
    this.selection = new Coordinate(0, 0);
    initialize();
  }
  
  //build an artificial map for testing
  public void initialize(){
    
    ArrayList<Planet> planets1 = new ArrayList<Planet>();
    planets1.add(new Planet("Planet A", Planet.planetType.Agri));
    planets1.add(new Planet("Planet B", Planet.planetType.Civilized));
    planets1.add(new Planet("Planet C", Planet.planetType.PenalColony));
    
    ArrayList<Planet> planets2 = new ArrayList<Planet>();
    planets2.add(new Planet("Planet A", Planet.planetType.Forge));
    
    ArrayList<Planet> planets3 = new ArrayList<Planet>();
    planets3.add(new Planet("Planet A", Planet.planetType.Agri));
    planets3.add(new Planet("Planet B", Planet.planetType.Civilized));
    planets3.add(new Planet("Planet C", Planet.planetType.PenalColony));
    planets3.add(new Planet("Planet D", Planet.planetType.Forge));
    
    ArrayList<Planet> planets4 = new ArrayList<Planet>();
    planets4.add(new Planet("Planet A", Planet.planetType.PenalColony));
    planets4.add(new Planet("Planet B", Planet.planetType.Agri));
    planets4.add(new Planet("Planet C", Planet.planetType.Civilized));
    planets4.add(new Planet("Planet D", Planet.planetType.Pirate));
    planets4.add(new Planet("Planet E", Planet.planetType.Hive));
    planets4.add(new Planet("Planet F", Planet.planetType.Home));
    planets4.add(new Planet("Planet G", Planet.planetType.Mining));
    planets4.add(new Planet("Planet H", Planet.planetType.Uninhabited));
    
    StarSystem sys1 = null, sys2 = null, sys3 = null, sys4 = null;
    try{
    sys1 = new StarSystem(1, 1, "System 1");
    sys2 = new StarSystem(10, 10, "System 2");
    sys3 = new StarSystem(5, 6, "System 3");
    sys4 = new StarSystem(6, 4, "System 4");
    }catch( DrawingPositionException e){
      System.err.println(e);
    }
    sys1.setPlanets(planets1);
    sys2.setPlanets(planets2);
    sys3.setPlanets(planets3);
    sys4.setPlanets(planets4);
    
    theMap.addSystem(sys1);
    theMap.addSystem(sys2);
    theMap.addSystem(sys3);
    theMap.addSystem(sys4);
  }
  
  public void paint(Graphics g){
     Graphics2D g2 = (Graphics2D)g;
     g2.setColor(Color.black);
     g2.fill(new Rectangle(0, 0, this.getWidth(), this.getHeight()));
     g2.setColor(Color.white);
     
     //draw currently selected system
     StarSystem selected = SystemTable[selection.x][selection.y];
     if(!drawSector && selected != null){
    	 //debug grid draw
//    	 for(int i = 0; i < MAP_SIZE_X; i+=PLANET_SIZE){
//    		 for(int j = 0; j < MAP_SIZE_Y; j+=PLANET_SIZE){
//    			 g2.drawLine(i, j, i, MAP_SIZE_Y);
//    			 g2.drawLine(i, j, MAP_SIZE_X, j );
//    		 }
//    	 }
         //draw star
         g2.fillOval(4*MAP_RES, 4*MAP_RES, (MAP_RES*2) - PADDING, (MAP_RES*2) - PADDING);
         for(int i = 0; i < selected.getSystemPlanets().size(); i++){
           //draw orbits
           g2.setStroke(OrbitStroke);
           g2.drawOval((i)*PLANET_SIZE, (i)*PLANET_SIZE, MAP_SIZE_X - (2*(i*PLANET_SIZE)), MAP_SIZE_Y - (2*(i*PLANET_SIZE)));
           //draw Planets
           g2.setStroke(PlanetStroke);
           int x = planetPositions[i].x*PLANET_SIZE - (PLANET_SIZE/2);
           int y = planetPositions[i].y*PLANET_SIZE - (PLANET_SIZE/2);
           g2.setColor(Color.black);
           g2.fillOval(x, y, PLANET_SIZE, PLANET_SIZE);
           g2.setColor(Color.white);
           g2.drawOval(x, y, PLANET_SIZE, PLANET_SIZE);
         }
         g2.setFont(new Font("Monospace", Font.BOLD, 14));
         g2.drawString(selected.toString(), 0, MAP_SIZE_Y - (g2.getFontMetrics().getAscent()/2));
     }
     //draw star systems on sector map
     else{
         ArrayList<StarSystem> systems = this.theMap.getSystems();
         g2.setStroke(SystemStroke);
         SystemTable = newSysTable(); //clear system table
         for(int i = 0; i < systems.size(); i++){
           SystemTable[systems.get(i).getX()-1][systems.get(i).getY()-1] = 
               systems.get(i); //make entries in system table
           //scale positions to the map size
           int x = (systems.get(i).getX()-1)*MAP_RES;
           int y = (systems.get(i).getY()-1)*MAP_RES;
           g2.drawOval(x + (PADDING/2), y + (PADDING/2), 
               MAP_RES - PADDING, MAP_RES - PADDING);
         }
     }
     
  }
  
  private Coordinate polarToCart( int r, int theta){
    Coordinate ret = new Coordinate(0, 0);
    ret.x = (int)Math.cos( Math.toRadians(theta))*r;
    ret.y = (int)Math.sin( Math.toRadians(theta))*r;
    return ret;
  }
  
  public StarSystem[][] newSysTable(){
    return new StarSystem[MAP_SIZE_X/MAP_RES][MAP_SIZE_Y/MAP_RES];
  }
  
  public Dimension getPreferredScrollableViewportSize(){
	  return new Dimension(MAP_SIZE_X, MAP_SIZE_Y);
  }
  
  public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
		  int direction){
	  return 5;
  }
  
  public boolean getScrollableTracksViewportWidth(){
	  return false;
  }
  
  public boolean getScrollableTracksViewportHeight(){
	  return false;
  }
  
  public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
		  int direction){
	  return 5;
  }
  
  public void mouseClicked(MouseEvent e){
    int x, y;
    x = e.getX();
    y = e.getY();
    x /= MAP_RES;
    y /= MAP_RES;
    if(drawSector){
    System.out.println("System "+SystemTable[x][y]+" selected");
      selection.x = x;
      selection.y = y;
      drawSector = false;
    }else{
      drawSector = true; //TODO make a button for this
    }
    this.repaint();
  }
  
  public void mousePressed(MouseEvent e){
  }
  
  public void mouseReleased(MouseEvent e){
  }
  
  public void mouseEntered(MouseEvent e){
  }
  
  public void mouseExited(MouseEvent e){
    this.setToolTipText("");
  }
  
  public void mouseMoved(MouseEvent e){
    int x, y;
    x = e.getX();
    y = e.getY();
    x /= MAP_RES;
    y /= MAP_RES;
    if(drawSector){
      if(SystemTable[x][y] != null ){
        this.setToolTipText(""+SystemTable[x][y]);
      }
    }else{
      //TODO print planet name
    }
    this.repaint();
  }
  
  public void mouseDragged(MouseEvent e){
  }
  public Map getMap(){ return this.theMap; }
}
